import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                             QHBoxLayout, QCalendarWidget, QLabel, QDialog, 
                             QFrame, QTimeEdit, QPushButton, QToolButton,
                             QDateTimeEdit, QStyle)
from PySide6.QtCore import Qt, QDate, QTime, QDateTime, QPoint, Signal
from PySide6.QtGui import QBrush, QColor, QTextCharFormat, QCursor, QFont


class DateTimeRangeSelector(QDialog):
    """
    日期时间范围选择器对话框
    提供可视化的日历和时间选择界面，支持选择起始和结束日期时间范围
    """
    
    def __init__(self, parent=None, start_datetime=None, end_datetime=None):
        super().__init__(parent)
        # 设置无边框和弹窗样式，背景透明
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        # 初始化日期时间，默认起始时间为当前时间前一天，结束时间为当前时间
        self.start_datetime = start_datetime or QDateTime.currentDateTime().addDays(-1)
        self.end_datetime = end_datetime or QDateTime.currentDateTime()
        
        # 存储选择的起始和结束日期
        self.start_date = self.start_datetime.date()
        self.end_date = self.end_datetime.date()
        
        # 初始化UI界面
        self.init_ui()
        
    def init_ui(self):
        """初始化用户界面"""
        # 创建主框架，用于设置样式
        main_frame = QFrame(self)
        main_frame.setObjectName("popupFrame")
        main_frame.setStyleSheet("""
            #popupFrame {
                background-color: white;
                border-radius: 8px;
                border: 1px solid #cccccc;
                box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.15);
            }
        """)
        
        # 主布局
        main_layout = QVBoxLayout(main_frame)
        main_layout.setContentsMargins(12, 12, 12, 12)
        main_layout.setSpacing(10)
        
        # 日期选择区域布局
        select_range_layout = QHBoxLayout()
        
        # 起始日期和时间布局
        start_time_layout = QVBoxLayout()
        self.start_calendar = RangeCalendarWidget(self)
        # 设置起始日历的日期范围
        self.start_calendar.set_date_range(self.start_date, self.end_date)
        self.start_calendar.clicked.connect(self.on_date_clicked)
        self.start_calendar.selectionChanged.connect(self.on_start_date_changed)
        
        # 起始时间编辑器
        self.start_time_edit = QTimeEdit()
        self.start_time_edit.setDisplayFormat("HH:mm:ss")
        self.start_time_edit.setTime(self.start_datetime.time())
        self.start_time_edit.timeChanged.connect(self.on_start_time_changed)
        
        start_time_layout.addWidget(self.start_calendar)
        start_time_layout.addWidget(self.start_time_edit)
        
        # 结束日期和时间布局
        end_time_layout = QVBoxLayout()
        self.end_calendar = RangeCalendarWidget(self)
        self.end_calendar.set_date_range(self.start_date, self.end_date)
        self.end_calendar.clicked.connect(self.on_date_clicked)
        self.end_calendar.selectionChanged.connect(self.on_end_date_changed)
        
        # 结束时间编辑器
        self.end_time_edit = QTimeEdit()
        self.end_time_edit.setDisplayFormat("HH:mm:ss")
        self.end_time_edit.setTime(self.end_datetime.time())
        self.end_time_edit.timeChanged.connect(self.on_end_time_changed)
        
        end_time_layout.addWidget(self.end_calendar)
        end_time_layout.addWidget(self.end_time_edit)
        
        # 按钮区域布局
        button_layout = QHBoxLayout()
        ok_button = QPushButton("确定")
        ok_button.setStyleSheet("background-color: #4a86e8; color: white;")
        ok_button.clicked.connect(self.accept)
        
        cancel_button = QPushButton("取消")
        cancel_button.setStyleSheet("background-color: #e6e6e6;")
        cancel_button.clicked.connect(self.reject)
        
        button_layout.addWidget(ok_button)
        button_layout.addWidget(cancel_button)
        
        # 组装所有布局
        select_range_layout.addLayout(start_time_layout)
        select_range_layout.addLayout(end_time_layout)
        main_layout.addLayout(select_range_layout)
        main_layout.addLayout(button_layout)
        
        # 设置主窗口布局
        outer_layout = QVBoxLayout(self)
        outer_layout.setContentsMargins(0, 0, 0, 0)
        outer_layout.addWidget(main_frame)
    
    def on_date_clicked(self, date):
        """
        处理日期点击事件
        规则：
        1. 未选择起始日期或已选择结束日期时，点击设置为起始日期
        2. 已选择起始日期时，点击设置为结束日期
        3. 确保结束日期不早于起始日期
        """
        if not self.start_date or self.end_date:
            # 设置新的起始日期
            self.start_date = date
            self.end_date = None
        else:
            # 设置结束日期，确保不早于起始日期
            if date < self.start_date:
                self.end_date = self.start_date
                self.start_date = date
            else:
                self.end_date = date
                
        # 更新日历显示样式
        self.start_calendar.update_cell_formats()
        self.end_calendar.update_cell_formats()
        # 更新日期时间
        self.on_start_date_changed()
        self.on_end_date_changed()
        
    def on_start_date_changed(self):
        """处理起始日期变化事件，确保起始日期不晚于结束日期"""
        if self.start_date:
            self.start_datetime.setDate(self.start_date)
            # 更新结束日历的可选范围
            self.end_calendar.set_date_range(self.start_date, self.end_calendar.end_date)
            
            # 确保起始日期不晚于结束日期
            if self.start_datetime > self.end_datetime:
                self.end_datetime.setDate(self.start_date)
                self.end_calendar.set_date_range(self.start_date, self.start_date)
                self.end_time_edit.setTime(self.start_datetime.time())
    
    def on_end_date_changed(self):
        """处理结束日期变化事件，确保结束日期不早于起始日期"""
        if self.end_date:
            self.end_datetime.setDate(self.end_date)
            # 更新起始日历的可选范围
            self.start_calendar.set_date_range(self.start_calendar.start_date, self.end_date)
            
            # 确保结束日期不早于起始日期
            if self.end_datetime < self.start_datetime:
                self.start_datetime.setDate(self.end_date)
                self.start_calendar.set_date_range(self.end_date, self.end_date)
                self.start_time_edit.setTime(self.end_datetime.time())
    
    def on_start_time_changed(self):
        """处理起始时间变化事件，确保起始时间不晚于结束时间"""
        selected_time = self.start_time_edit.time()
        self.start_datetime.setTime(selected_time)
        
        # 确保起始时间不晚于结束时间
        if self.start_datetime > self.end_datetime:
            self.end_datetime.setTime(selected_time)
            self.end_time_edit.setTime(selected_time)
    
    def on_end_time_changed(self):
        """处理结束时间变化事件，确保结束时间不早于起始时间"""
        selected_time = self.end_time_edit.time()
        self.end_datetime.setTime(selected_time)
        
        # 确保结束时间不早于起始时间
        if self.end_datetime < self.start_datetime:
            self.start_datetime.setTime(selected_time)
            self.start_time_edit.setTime(selected_time)
    
    def get_selected_range(self):
        """获取选择的日期时间范围"""
        return self.start_datetime, self.end_datetime


class RangeCalendarWidget(QCalendarWidget):
    """
    支持日期范围选择和高亮显示的日历控件
    能够可视化显示起始日期、结束日期和中间的日期范围
    """
    
    def __init__(self, parent: DateTimeRangeSelector = None):
        super().__init__()
        self.setGridVisible(True)
        self._parent = parent  # 关联到父窗口
        self.start_date = None  # 起始日期
        self.end_date = None    # 结束日期
        
    def set_date_range(self, start_date, end_date):
        """
        设置日期范围
        :param start_date: 起始日期
        :param end_date: 结束日期
        """
        # 确保起始日期不晚于结束日期
        if start_date and end_date and start_date > end_date:
            start_date, end_date = end_date, start_date
            
        self.start_date = start_date
        self.end_date = end_date
        self.update_cell_formats()  # 更新显示格式
    
    
    def update_cell_formats(self):
        """更新单元格格式，高亮显示选择的日期范围"""
        # 重置所有日期格式为默认样式
        normal_format = QTextCharFormat()
        normal_format.setBackground(self.palette().base())
        normal_format.setForeground(self.palette().text())
        
        # 获取当前显示月份的第一天和最后一天
        first_day = QDate(self.yearShown(), self.monthShown(), 1)
        last_day = QDate(self.yearShown(), self.monthShown(), first_day.daysInMonth())
        
        # 重置当月所有日期格式
        for d in range(1, last_day.day() + 1):
            date = QDate(self.yearShown(), self.monthShown(), d)
            self.setDateTextFormat(date, normal_format)
        
        # 如果没有选择起始日期，直接返回
        if not self._parent.start_date:
            return
            
        # 设置起始日期样式：蓝色背景，白色文字
        start_format = QTextCharFormat()
        start_format.setBackground(QBrush(QColor(74, 134, 232)))
        start_format.setForeground(QBrush(QColor(255, 255, 255)))
        self.setDateTextFormat(self._parent.start_date, start_format)
        
        # 如果只有起始日期，没有结束日期，直接返回
        if not self._parent.end_date:
            return
            
        # 设置中间日期范围样式：浅蓝色背景
        if self._parent.start_date != self._parent.end_date:
            range_format = QTextCharFormat()
            range_format.setBackground(QBrush(QColor(200, 220, 255)))
            range_format.setForeground(self.palette().text())
            
            # 遍历起始日期到结束日期之间的所有日期
            current_date = QDate(self._parent.start_date)
            while current_date != self._parent.end_date:
                current_date = current_date.addDays(1)
                # 只更新当前显示月份的日期
                if current_date.month() == self.monthShown():
                    self.setDateTextFormat(current_date, range_format)
        
        # 设置结束日期样式：蓝色背景，白色文字
        end_format = QTextCharFormat()
        end_format.setBackground(QBrush(QColor(74, 134, 232)))
        end_format.setForeground(QBrush(QColor(255, 255, 255)))
        self.setDateTextFormat(self._parent.end_date, end_format)
        


class DateTimeRangeEdit(QWidget):
    """
    日期时间范围编辑控件
    提供输入框显示和下拉选择功能，支持通过信号返回选择的日期时间范围
    """
    
    # 定义信号，当选择范围变化时发射
    range_selected = Signal(QDateTime, QDateTime)
    
    def __init__(self, parent=None, start_datetime=None, end_datetime=None):
        super().__init__(parent)
        # 初始化日期时间，默认起始时间为当天0点，结束时间为当天23:59:59
        self.start_datetime: QDateTime = start_datetime or QDateTime(QDate.currentDate(), QTime(0, 0, 0))
        self.end_datetime: QDateTime = end_datetime or QDateTime(QDate.currentDate(), QTime(23, 59, 59))
        
        # 初始化UI界面
        self.init_ui()
        
    def init_ui(self):
        """初始化用户界面"""
        # 水平布局
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 起始日期时间编辑框
        self.start_edit = QDateTimeEdit()
        self.start_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
        self.start_edit.setDateTime(self.start_datetime)
        self.start_edit.setReadOnly(True)  # 设置为只读，防止手动输入
        self.start_edit.setCursor(Qt.PointingHandCursor)  # 鼠标悬停时显示手型
        self.start_edit.mousePressEvent = self.show_selector  # 点击时显示选择器
        
        # 分隔符
        separator = QLabel("至")
        separator.setAlignment(Qt.AlignCenter)
        separator.setFixedWidth(20)
        
        # 结束日期时间编辑框
        self.end_edit = QDateTimeEdit()
        self.end_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
        self.end_edit.setDateTime(self.end_datetime)
        self.end_edit.setReadOnly(True)  # 设置为只读，防止手动输入
        self.end_edit.setCursor(Qt.PointingHandCursor)  # 鼠标悬停时显示手型
        self.end_edit.mousePressEvent = self.show_selector  # 点击时显示选择器
        
        # 下拉按钮
        self.dropdown_button = QToolButton()
        self.dropdown_button.setIcon(self.style().standardIcon(QStyle.SP_ArrowDown))
        self.dropdown_button.clicked.connect(self.show_selector)
        
        # 添加到布局
        layout.addWidget(self.start_edit)
        layout.addWidget(separator)
        layout.addWidget(self.end_edit)
        layout.addWidget(self.dropdown_button)
        
        # 设置样式
        self.setStyleSheet("""
            QDateTimeEdit, QToolButton {
                border: 1px solid #cccccc;
                border-radius: 4px;
                padding: 4px;
            }
            QToolButton {
                background-color: #f0f0f0;
            }
            QToolButton:hover {
                background-color: #e0e0e0;
            }
        """)
    
    def show_selector(self, event=None):
        """
        显示日期时间范围选择器对话框
        优化显示位置，确保对话框不会超出屏幕范围
        """
        # 创建选择器对话框
        selector = DateTimeRangeSelector(self, self.start_datetime, self.end_datetime)
        
        # 调整对话框大小
        selector.adjustSize()
        dialog_width = selector.width()
        dialog_height = selector.height()
        
        # 获取屏幕和控件位置信息
        screen = QApplication.primaryScreen().geometry()
        widget_pos = self.mapToGlobal(QPoint(0, self.height()))
        cursor_pos = QCursor.pos()
        
        # 计算对话框显示位置，优先显示在控件下方
        x = widget_pos.x()
        y = widget_pos.y()
        
        # 检查右侧边界
        if x + dialog_width > screen.right():
            x = screen.right() - dialog_width
            
        # 检查底部边界，如果空间不足则显示在控件上方
        if y + dialog_height > screen.bottom():
            y = widget_pos.y() - dialog_height - self.height()
            # 如果上方空间仍不足，显示在屏幕顶部
            if y < screen.top():
                y = screen.top()
        
        # 设置对话框位置并显示
        selector.move(x, y)
        
        # 如果用户点击确定，更新选择的日期时间范围
        if selector.exec():
            self.start_datetime, self.end_datetime = selector.get_selected_range()
            self.start_edit.setDateTime(self.start_datetime)
            self.end_edit.setDateTime(self.end_datetime)
            # 发射范围选择信号
            self.range_selected.emit(self.start_datetime, self.end_datetime)

    def get_range(self):
        """
        获取选择的时间范围（时间戳形式）
        :return: 起始时间戳和结束时间戳（秒）
        """
        return (self.start_datetime.toSecsSinceEpoch(), 
                self.end_datetime.toSecsSinceEpoch())


class MainWindow(QMainWindow):
    """主窗口示例，用于演示日期时间范围选择器的使用"""
    
    def __init__(self):
        super().__init__()
        self.setWindowTitle("日期时间范围选择器示例")
        self.setGeometry(100, 100, 600, 300)
        
        # 创建中央部件和布局
        central_widget = QWidget()
        layout = QVBoxLayout(central_widget)
        
        # 添加说明标签
        info_label = QLabel("点击输入框或右侧按钮选择日期时间范围：")
        layout.addWidget(info_label)
        
        # 添加日期时间范围选择器
        self.range_edit = DateTimeRangeEdit()
        self.range_edit.range_selected.connect(self.on_range_selected)
        layout.addWidget(self.range_edit)
        
        # 添加结果显示标签
        self.result_label = QLabel("未选择日期时间范围")
        self.result_label.setWordWrap(True)
        self.result_label.setFont(QFont("Arial", 12))
        layout.addWidget(self.result_label)
        
        self.setCentralWidget(central_widget)
    
    def on_range_selected(self, start_datetime, end_datetime):
        """
        处理日期时间范围选择事件
        :param start_datetime: 起始日期时间
        :param end_datetime: 结束日期时间
        """
        # 计算时间间隔（小时）
        duration = abs(end_datetime.secsTo(start_datetime) / 3600.0)
        
        # 更新显示结果
        self.result_label.setText(
            f"开始时间: {start_datetime.toString('yyyy-MM-dd HH:mm:ss')}\n"
            f"结束时间: {end_datetime.toString('yyyy-MM-dd HH:mm:ss')}\n"
            f"时间间隔: {duration:.2f} 小时"
        )


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())